Bezier Flächen (Bezier Patches)
Geschrieben von: David Nikdel ( ogapo@ithink.net. )
Dieses Tutorial soll Sie in Bezier Flächen einführen, in der Hoffnung, dass jemand der künstlerisch mehr begabt als ich ist, etwas ziemlich cooles damit macht und es uns zeigt. Es ist nicht als eine komplette Bezier Patch Bibliothek beabsichtig, sondern viel mehr als Einstiegspunkt, um Sie damit vertraut zu machen, wie gekrümmte Oberflächen arbeiten. Außerdem, da dies hier nur zu Information dient, habe ich nicht die ganz korrekte Terminologie zugunsten des Verständnisses benutzt; Ich hoffe, dass jeder damit einverstanden ist. Für jeden, der bereits mit Beziers vertraut ist und das hier nur liest, um zu sehen, ob ich es verhunzt habe: schämen Sie sich ;-), aber wenn Sie etwas völlig falsches finden, lassen sie es mich oder NeHe wissen, da ja keiner perfekt ist, oder nicht? Oh, noch eine weitere Sache: der komplette Code wurde in keinster Weise über meinen normalen Programmierstil hinaus optimiert. Ich möchte, dass jeder genau weiß, was vorgeht. Nun denn, ich denke das ist genug zur Einführung. Auf geht's!
Die Mathematik - ::dämonische Musik:: (achtung, etwas längerer Abschnitt)
Ok, es dürfte recht schwierig sein Beziers zu verstehen, ohne die grundlegendsten Verständnisse der Mathematik, die dahinter steht, wie dem auch sei, wenn Sie diesen Teil gerade nicht lesen wollen oder Sie bereits die Mathematik kennen, können Sie diesen Teil überspringen. Ich werde mit der Beschreibung beginnen, was eine Bezier Kurve ist und dann dahin übergehen, wie man ein Bezier Patch erzeugt.
Komisch ist, dass, wenn Sie jemals ein Graphic-Programm verwendet haben, Sie bereits mit Bezier Kurven vertraut sind, wenn auch wahrscheinlich nicht unter diesem Namen. Sie sind die verbreitetste Methode um krumme Linien zu zeichnen und werden in der Regel durch eine Reihe von Punkten repräsentiert, wobei jeder 2 Punkte hat, die die Tangente repräsentieren, am Punkt links und rechts davon. So sieht das aus:
Das ist die einfachst mögliche Bezier Kurve (längere werden durch viele kleine gebildet (häufig ohne dass der Benutzer es merkt)). Diese Kurve ist tatsächlich nur durch 4 Punkte definiert, das wären die 2 End-Kontroll-Punkte und die 2 mittleren Kontroll-Punkte. Für den Computer sind alle Punkte gleich, aber um uns das Designen zu erleichtern verbinden wir jeweils die ersten und letzten zwei, da diese immer Tangenten zum Endpunkt sind. Die Kurve ist eine parametrische Kurve und wird gezeichnet, indem Punkte, die direkt auf der Kuve liegen, mit geraden Linien verbunden werden. Auf diesem Weg können Sie die Auflösung des Patch kontrollieren (und den rechen Aufwand). Der gebräuchlichste Weg ist es bei höherer Entfernung weniger Details zu verwenden und je näher der Betrachter kommt, entsprechend mehr, so dass es immer nach einer perfekt gekrümmten Oberfläche aussieht, mit dem geringsten Rechenaufwand.
Bezier Kurven basieren auf einer Basis-Funktion, von der kompliziertere Versionen abgeleitet werden. Hier die Funktion:
t + (1 - t) = 1
Klingt einfach, was? Nunja, ist es auch, das ist die einfachste Bezier Kurve, eine Kurve ersten Grades. Wie Sie vielleicht aus der Terminologie erkennen können, handelt es sich bei Bezier Kurven um Polynome und wie wir uns aus der Algebra erinnen, ein Polynom ersten Grades ist einfach nur eine gerade Linie; nicht gerade interessant. Nun, da die Basis-Funktion für alle Nummer t wahr ist, können wir quadrieren, sie in die dritte Potenz erheben, was auch immer, es würde immer noch wahr sein, richtig? Versuchen wir's mit der dritten Potenz.
(t + (1-t))^3 = 1^3
t^3 + 3*t^2*(1-t) + 3*t*(1-t)^2 + (1-t)^3 = 1
Das ist die Gleichung die wir benutzen werden, um das gängiste Bezier zu berechnen, die Bezier Kurve dritten Grades. Sie ist am gängisten für zwei Gründe, a) es ist das Polynomal des kleinsten Grades, das nicht notwendigerweise in einer Ebene lieben muss (es gibt 4 Kontrollpunkte) und b) die Tangenten Linien an den Seiten sind nicht voneinander abhängig (bei 2ten Grades gäbe es nur 3 Kontrollpunkte). Sehen Sie bereits die Bezier Kurve? Hehe, ich auch nicht, deshalb muss ich eine Sache noch hinzufügen.
Ok, da die gesamte linke Seite gleich 1 ist, ist es sicher anzunehmen, dass wenn Sie alle Komponenten addieren, Sie immer noch gleich 1 sind. Hört sich das danach, ob wir damit entscheiden können, wieviel jeder Kontrollpunkt benutzt wird, um einen Punkt in der Kurve zu berechnen? (hinweise: sagen Sie einfach ja ;-) ) Sie haben recht! Wenn wir den Wert eines Punktes einige Prozent entlängs der Kurve berechnen wollen, multiplizieren wir einfach jeden Teil mit einem Kontrollpunkt (als Vektor) und finden die Summe. Generell kann man sagen, wir arbeiten mit 0 <= t <= 1, aber das ist technisch gesehen nicht notwendig. Verwirrt? Hier ist die Funktion:
P1*t^3 + P2*3*t^2*(1-t) + P3*3*t*(1-t)^2 + P4*(1-t)^3 = Pneu
Da Polynome immer stetig sind, ist das ein guter Weg, um zwischen den 4 Punkten zu morphen. Die einzigen Punkte die tatsächlich erreicht werden, sind P1 und P4 wenn t = 1, bzw. gleich 0 ist.
Nun, das ist alles schön und gut, aber wie kann ich das in 3D verwenden werden Sie sich fragen. Nun, eigentlich ist es recht einfach, um eine Bezier Patch zu formen, brauchen Sie 16 Kontrollpunkte (4*4) und 2 Variablen t und v. Daraus berechnen Sie ein Punkte bei v entlängs der 4 parallelen Kurven, dann benutzen Sie diese 4 Punkte um eine neue Kurve zu machen und berechnen t entlängs dieser Kurve. Indem wir genügend dieser Punkte berechnen, können wir Triangle Strips zeichnen und diese miteinander verbinden, woraus unser Bezier Patch ensteht.
Ich denke das war erst mal genug Mathe für jetzt, auf zum Code!
#include < windows.h> // Header Datei für Windows
#include < math.h> // Header Datei für Math Library Routines
#include < stdio.h> // Header Datei für Standard I/O Routinen
#include < stdlib.h> // Header Datei für Standard Library
#include < gl\gl.h> // Header Datei für die OpenGL32 Library
#include < gl\glu.h> // Header Datei für die GLu32 Library
#include < gl\glaux.h> // Header Datei für dieGlaux Library
typedef struct point_3d { // Struktur für einen 3-Dimensionalen Punkt ( NEU )
double x, y, z;
} POINT_3D;
typedef struct bpatch { // Struktur für ein Bezier Patch 3ten Grades ( NEU )
POINT_3D anchors[4][4]; // 4x4 Gitter an Anker-Punkten
GLuint dlBPatch; // Display Liste für Bezier Patch
GLuint texture; // Textur für das Patch
} BEZIER_PATCH;
HDC hDC=NULL; // Privater GDI Device Context
HGLRC hRC=NULL; // Permanenter Rendering Context
HWND hWnd=NULL; // Enthält unser Fenster-Handle
HINSTANCE hInstance; // Enthält die Instanz der Applikation
DEVMODE DMsaved; // speichert die vorherigen Bildschirmeinstellungen ( NEU )
bool keys[256]; // Array das für die Tastatur Routine verwendet wird
bool active=TRUE; // Fenster Aktiv Flag standardmäßig auf TRUE gesetzt
bool fullscreen=TRUE; // Fullscreen Flag ist standardmäßig auf TRUE gesetzt
GLfloat rotz = 0.0f; // Rotation um die Z-Achse
BEZIER_PATCH mybezier; // Das Bezier Patch welches wir verwenden werden ( NEU )
BOOL showCPoints=TRUE; // schaltet das Anzeigen des Kontroll-Punkt-Gitters ( NEU )
int divs = 7; // Anzahl der Intrapolationen(steuert Polygon Auflösung) ( NEU )
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Deklaration für WndProc
// Addiert 2 Punkte. Benutzen Sie nicht einfach '+' ;)
POINT_3D pointAdd(POINT_3D p, POINT_3D q) {
p.x += q.x; p.y += q.y; p.z += q.z;
return p;
}
// Multiplziert einen Punkt un eine Konstante. Benutzen Sie nicht einfach '*'
POINT_3D pointTimes(double c, POINT_3D p) {
p.x *= c; p.y *= c; p.z *= c;
return p;
}
// Funktion zur schnellen Punkt-Erzeugung
POINT_3D makePoint(double a, double b, double c) {
POINT_3D p;
p.x = a; p.y = b; p.z = c;
return p;
}
// berechnet Polygon dritten Grades basierend auf einem Array aus 4 Punkten
// und einer einzelnen Variable (u) welche in der Regel zwischen 0 und 1 ist
POINT_3D Bernstein(float u, POINT_3D *p) {
POINT_3D a, b, c, d, r;
a = pointTimes(pow(u,3), p[0]);
b = pointTimes(3*pow(u,2)*(1-u), p[1]);
c = pointTimes(3*u*pow((1-u),2), p[2]);
d = pointTimes(pow((1-u),3), p[3]);
r = pointAdd(pointAdd(a, b), pointAdd(c, d));
return r;
}
// erzeugt eine Display Liste basierend auf den Daten aus dem Patch
// und der Anzahl der Divisionen
GLuint genBezier(BEZIER_PATCH patch, int divs) {
int u = 0, v;
float py, px, pyold;
GLuint drawlist = glGenLists(1); // erzeuge die Display Liste
POINT_3D temp[4];
POINT_3D *last = (POINT_3D*)malloc(sizeof(POINT_3D)*(divs+1));
// Array an Punkten, um die erste Linie Polygone zu kennzeichnen
if (patch.dlBPatch != NULL) // werde alle alten Display Listen los
glDeleteLists(patch.dlBPatch, 1);
temp[0] = patch.anchors[0][3]; // die erste hergeleitete Kurve (entlängs der X-Achse)
temp[1] = patch.anchors[1][3];
temp[2] = patch.anchors[2][3];
temp[3] = patch.anchors[3][3];
for (v=0;v<=divs;v++) { // erzeuge die erste Linien an Punkten
px = ((float)v)/((float)divs); // Prozent entlängs der Y-Achse
// benutze die 4 Punkte aus der hergeleiteten Kurve, um die Punkte entlängs der Kurve zu berechnen
last[v] = Bernstein(px, temp);
}
glNewList(drawlist, GL_COMPILE); // Starte eine neue Display Liste
glBindTexture(GL_TEXTURE_2D, patch.texture); // Binde die Texturen
for (u=1;u<=divs;u++) {
py = ((float)u)/((float)divs); // Prozent entlängs der Y-Achse
pyold = ((float)u-1.0f)/((float)divs); // Prozent entlängs der alten Y-Achse
temp[0] = Bernstein(py, patch.anchors[0]); // berechne neue Bezier Punkte
temp[1] = Bernstein(py, patch.anchors[1]);
temp[2] = Bernstein(py, patch.anchors[2]);
temp[3] = Bernstein(py, patch.anchors[3]);
glBegin(GL_TRIANGLE_STRIP); // Beginne einen neuen Triangle Strip
for (v=0;v<=divs;v++) {
px = ((float)v)/((float)divs); // Prozent entlängs der X-Achse
glTexCoord2f(pyold, px); // wende die alten Texturkoordinaten an
glVertex3d(last[v].x, last[v].y, last[v].z); // alter Punkt
last[v] = Bernstein(px, temp); // erzeuge neuen Punkt
glTexCoord2f(py, px); // wende die neuen Texturkoordinaten an
glVertex3d(last[v].x, last[v].y, last[v].z); // Neuer Punkt
}
glEnd(); // BEENDE Triangle Strip
}
glEndList(); // BEENDE die Liste
free(last); // gebe das alte Vertices-Array frei
return drawlist; // gebe die Display Liste zurück
}
void initBezier(void) {
mybezier.anchors[0][0] = makePoint(-0.75, -0.75, -0.50); // Setze die Bezier Vertices
mybezier.anchors[0][1] = makePoint(-0.25, -0.75, 0.00);
mybezier.anchors[0][2] = makePoint( 0.25, -0.75, 0.00);
mybezier.anchors[0][3] = makePoint( 0.75, -0.75, -0.50);
mybezier.anchors[1][0] = makePoint(-0.75, -0.25, -0.75);
mybezier.anchors[1][1] = makePoint(-0.25, -0.25, 0.50);
mybezier.anchors[1][2] = makePoint( 0.25, -0.25, 0.50);
mybezier.anchors[1][3] = makePoint( 0.75, -0.25, -0.75);
mybezier.anchors[2][0] = makePoint(-0.75, 0.25, 0.00);
mybezier.anchors[2][1] = makePoint(-0.25, 0.25, -0.50);
mybezier.anchors[2][2] = makePoint( 0.25, 0.25, -0.50);
mybezier.anchors[2][3] = makePoint( 0.75, 0.25, 0.00);
mybezier.anchors[3][0] = makePoint(-0.75, 0.75, -0.50);
mybezier.anchors[3][1] = makePoint(-0.25, 0.75, -1.00);
mybezier.anchors[3][2] = makePoint( 0.25, 0.75, -1.00);
mybezier.anchors[3][3] = makePoint( 0.75, 0.75, -0.50);
mybezier.dlBPatch = NULL; // mache weiter und initialisiere dieses mit NULL
}
// Lade Bitmaps und konvertiere in Texturen
BOOL LoadGLTexture(GLuint *texPntr, char* name)
{
BOOL success = FALSE;
AUX_RGBImageRec *TextureImage = NULL;
glGenTextures(1, texPntr); // erzeuge 1 Textur
FILE* test=NULL;
TextureImage = NULL;
test = fopen(name, "r"); // Teste, ob die Datei existiert
if (test != NULL) { // wenn ja
fclose(test); // schließe die Datei
TextureImage = auxDIBImageLoad(name); // und lade die Textur
}
if (TextureImage != NULL) { // wenn sie geladen wurde
success = TRUE;
// normalerweise benutzt die Textur-Erzeugung Daten aus dem Bitmap
glBindTexture(GL_TEXTURE_2D, *texPntr);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX,
TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
TextureImage->data);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
if (TextureImage->data)
free(TextureImage->data);
return success;
}
int InitGL(GLvoid) // Der ganze Setup Kram für OpenGL kommt hier rein
{
glEnable(GL_TEXTURE_2D); // aktiviert Textur Mapping
glShadeModel(GL_SMOOTH); // aktiviert Smooth Shading
glClearColor(0.05f, 0.05f, 0.05f, 0.5f); // Schwarzer Hintergrund
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // aktiviert Depth Testing
glDepthFunc(GL_LEQUAL); // Die Art des auszuführenden Depth Test
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // wirklich nette Perspektiven Berechnungen
initBezier(); // Initialisiert das Bezier Kontroll-Gitter ( NEU )
LoadGLTexture(&(mybezier.texture), "./Data/NeHe.bmp"); // Lade die Textur ( NEU )
mybezier.dlBPatch = genBezier(mybezier, divs); // erzeuge Patch ( NEU )
return TRUE; // Initialisierung war in Ordnung
}
int DrawGLScene(GLvoid) { // Hier kommt der ganze Zeichnen-Kram hin
int i, j;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Lösche den Bildschirm und den Depth-Buffer
glLoadIdentity(); // Resette die aktuelle Modelview Matrix
glTranslatef(0.0f,0.0f,-4.0f); // 1.5 Einheiten nach Links dann 6.0 Einheiten in den Bildschirm hinein
glRotatef(-75.0f,1.0f,0.0f,0.0f);
glRotatef(rotz,0.0f,0.0f,1.0f); // Rotiere das Dreieck auf der Z-Achse
glCallList(mybezier.dlBPatch); // rufe die Bezier Display Liste auf
// Dies muss nur aktualisiert werden, wenn das Patch geändert wird
if (showCPoints) { // wenn das Gitter gezeichnet werden soll
glDisable(GL_TEXTURE_2D);
glColor3f(1.0f,0.0f,0.0f);
for(i=0;i<4;i++) { // zeichne die horizontalen Linien
glBegin(GL_LINE_STRIP);
for(j=0;j<4;j++)
glVertex3d(mybezier.anchors[i][j].x, mybezier.anchors[i][j].y, mybezier.anchors[i][j].z);
glEnd();
}
for(i=0;i<4;i++) { // zeichne die vertikalen Linien
glBegin(GL_LINE_STRIP);
for(j=0;j<4;j++)
glVertex3d(mybezier.anchors[j][i].x, mybezier.anchors[j][i].y, mybezier.anchors[j][i].z);
glEnd();
}
glColor3f(1.0f,1.0f,1.0f);
glEnable(GL_TEXTURE_2D);
}
return TRUE; // weiter geht's
}
GLvoid KillGLWindow(GLvoid) // Entferne das Fenster korrekt
{
if (fullscreen) // Sind wir im Fullscreen Modus?
{
if (!ChangeDisplaySettings(NULL,CDS_TEST)) { // wenn der Shortcut nicht funktionier ( NEU )
ChangeDisplaySettings(NULL,CDS_RESET); // mache es trotzdem (um die Werte aus der Registry zu holen) ( NEU )
ChangeDisplaySettings(&DMsaved,CDS_RESET); // ändere sie auf die gespeicherten Einstellungen ( NEU )
} else {
ChangeDisplaySettings(NULL,CDS_RESET); // Wenn es funktioniert, mache weiter ( NEU )
}
ShowCursor(TRUE); // Zeige den Maus-Zeiger
}
if (hRC) // Haben wir einen Rendering Context?
{
if (!wglMakeCurrent(NULL,NULL)) // Können wir den DC und RC Kontext freigeben?
{
MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC)) // Können wir den RC löschen?
{
MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
hRC=NULL; // Setze RC auf NULL
}
if (hDC && !ReleaseDC(hWnd,hDC)) // Können wir DC freigeben?
{
MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hDC=NULL; // Setze DC auf NULL
}
if (hWnd && !DestroyWindow(hWnd)) // Können wir das Fenster zerstören?
{
MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hWnd=NULL; // Setze hWnd auf NULL
}
if (!UnregisterClass("OpenGL",hInstance)) // Können wir die Klasse de-registrieren?
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL; // Setze hInstance auf NULL
}
}
// Dieser Code erzeugt unser OpenGL Fenster. Parameter sind: // title - Titel der im Fenster erscheinen soll // width - Breite des GL Fensters oder Fullscreen Modus // height - Höhe des GL Fensters oder Fullscreen Modus // bits - Anzahl der zu benutzenden Farben (8/16/24/32) // fullscreenflag - Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; // Enthält die Ergebnisse nachdem nach was passendem gesucht wurde
WNDCLASS wc; // Fenster Klassen Struktur
DWORD dwExStyle; // erweiterter Fenster-Stil
DWORD dwStyle; // Fenster-Stil
RECT WindowRect; // Enthält die obere linke / untere rechte Eckwerte des Rechtecks
WindowRect.left=(long)0; // Setze linken Wert auf 0
WindowRect.right=(long)width; // Setze rechten Wert auf die gewünschte Breite
WindowRect.top=(long)0; // Setze den oberen Wert auf 0
WindowRect.bottom=(long)height; // Setze unteren Wert auf die gewünschte Höhe
fullscreen=fullscreenflag; // Setze das globale Fullscreen Flag
hInstance = GetModuleHandle(NULL); // Ermittle die Instanz für unser Fenster
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Zeichne neu beim bewegen und eigener DC für's Fenster
wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc behandelt die Nachrichten
wc.cbClsExtra = 0; // Keine extra Fenster-Daten
wc.cbWndExtra = 0; // Keine extra Fenster-Daten
wc.hInstance = hInstance; // Setze die Instanz
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Lade das Standard-Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Lade den Pfeil-Zeiger
wc.hbrBackground = NULL; // es wird kein Hintergrund für GL benötigt
wc.lpszMenuName = NULL; // Wir wollen kein Menü
wc.lpszClassName = "OpenGL"; // Setze den Klassen-Namen
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DMsaved); // speichere den aktuellen Display Status ( NEU )
if (fullscreen) // Fullscreen Modus?
{
DEVMODE dmScreenSettings; // Device Modus
memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Stelle sicher, dass der Speicher geleert ist
dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Größe der Devmode Struktur
dmScreenSettings.dmPelsWidth = width; // ausgewählte Screen Breite
dmScreenSettings.dmPelsHeight = height; // ausgewählte Screen Höhe
dmScreenSettings.dmBitsPerPel = bits; // ausgewählte Bits Per Pixel
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
... Code Cut To Save Space (No Further Changes To This Function) ...
return TRUE; // Erfolg
}
int WINAPI WinMain( HINSTANCE hInstance, // Instanz
HINSTANCE hPrevInstance, // vorherige Instanz
LPSTR lpCmdLine, // Kommandozeilen Parameter
int nCmdShow) // Fenster Anzeige Status
{
MSG msg; // Windows Nachrichten Struktur
BOOL done=FALSE; // Bool Variable um die Schleife zu beenden
// Frage den Benutzer, in welchen Modus er starten will
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // Fenster-Modus
}
// erzeuge unser OpenGL Fenster
if (!CreateGLWindow("NeHe's Solid Object Tutorial",640,480,16,fullscreen))
{
return 0; // Beende, wenn Fenster nicht erzeugt wurde
}
while(!done) // Schleife die so lange läuft, bis done=TRUE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Wartet eine Nachricht?
{
if (msg.message==WM_QUIT) // Haben wir eine Nachricht zum beenden erhalten?
{
done=TRUE; // Wenn ja done=TRUE
}
else // Wenn nicht, bearbeite die Fenster-Nachrichten
{
TranslateMessage(&msg); // Übersetze die Nachricht
DispatchMessage(&msg); // bearbeite die Nachricht
}
}
else // Wenn da keine Nachrichten sind
{
// Zeichne die Szene. Schaue, ob ESC oder DrawGLScene() zum beenden signalisieren
if ((active && !DrawGLScene()) || keys[VK_ESCAPE]) // Activ? Soll beendet werden?
{
done=TRUE; // ESC oder DrawGLScene Signalisiert, dass Beendet werden soll
}
else // Es ist noch nicht Zeit zum beenden, zeichne Screen neu
{
SwapBuffers(hDC); // Swap Buffers (Double Buffering)
}
if (keys[VK_LEFT]) rotz -= 0.8f; // rotiere links ( NEU )
if (keys[VK_RIGHT]) rotz += 0.8f; // rotiere rechts ( NEU )
if (keys[VK_UP]) { // mehr Details ( NEU )
divs++;
mybezier.dlBPatch = genBezier(mybezier, divs); // akutalisiere die Fläche
keys[VK_UP] = FALSE;
}
if (keys[VK_DOWN] && divs > 1) { // weniger Details ( NEU )
divs--;
mybezier.dlBPatch = genBezier(mybezier, divs); // aktualisiere die Fläche
keys[VK_DOWN] = FALSE;
}
if (keys[VK_SPACE]) { // LEERTASTE invertiert showCPoints ( NEU )
showCPoints = !showCPoints;
keys[VK_SPACE] = FALSE;
}
if (keys[VK_F1]) // Wurde F1 gedrückt?
{
keys[VK_F1]=FALSE; // Wenn ja, setze Taste auf FALSE
KillGLWindow(); // Kill unser aktuelles Fenster
fullscreen=!fullscreen; // Wechsel zwischen Fullscreen und Fester-Modus
// Erzeuge unser OpenGL Fenster erneut
if (!CreateGLWindow("NeHe's Solid Object Tutorial",640,480,16,fullscreen))
{
return 0; // Beenden, wenn das Fenster nicht erzeugt wurde
}
}
}
}
// Shutdown
KillGLWindow(); // Kill das Fenster
return (msg.wParam); // Beende das Programm
}